/* 该程序中是在指定信号上阻塞的函数的对象实现，在调用simplesuspend前，
 * 程序调用initsuspend建立暂停信号的处理函数。程序调用restore将信号重新设置为之前的状态
 */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>

// 设置全局变量，从而可以方便调用
static struct sigaction oact;
static int isinitialized = 0;
static int signum;
// 将sigreceive设置为原子操作
static volatile sig_atomic_t sigreceive = 0;

/* 信号处理函数 */
static void catcher() {
    sigreceive = 1;
}


/* 初始化，建立暂停信号的处理函数 */
int initsuspend(int signo) {
    if (isinitialized) {
        return 0;
    }

    // 如果信号还没有被初始化，则进行初始化，使用sigaction进行捕获
    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = catcher;   // 设置信号处理函数

    // 保存初始化之前的信号
    if (sigfillset(&act.sa_mask) == -1 || sigaction(signo, &act, &oact) == -1)       
        return -1;
    
    signum = signo;
    isinitialized = 1;
    return 0;
}

/* 在指定信号上阻塞函数的对象实现 */
int simplesuspend(void) {
    sigset_t maskblocked, maskunblocked, maskold; 
    // 如果指定信号没有被设置，则退出
    if (!isinitialized) {
        errno = EINVAL;
        return -1;
    }

    if ( (sigprocmask(SIG_SETMASK, NULL, &maskblocked) == -1) ||
         (sigaddset(&maskblocked, signum) == -1) ||
         (sigprocmask(SIG_SETMASK, NULL, &maskunblocked) == -1) ||
         (sigdelset(&maskunblocked, signum) == -1))
         return -1;
    // 阻塞指定的信号
    if (sigprocmask(SIG_SETMASK, &maskblocked, &maskold) == -1 ) {
        return -1;
    }
    // 挂起信号
    while (sigreceive == 0) {
        sigsuspend(&maskunblocked);
    }
    sigreceive = 0;
    return sigprocmask(SIG_SETMASK, &maskold, NULL);    // 恢复阻塞之前的信号
}

/* 将信号重新设置为之前的状态 */
int restore(void) {
    if (isinitialized == 0) return 0;

    if(sigaction(signum, &oact, NULL) == -1) {
        return -1;
    }

    isinitialized = 0;
    return 0;
}


// 使用上面定义的函数挂起SIGUSR1
int main() {
    fprintf(stderr, "This is process %ld\n", (long)getpid());
    while(1) {
        fprintf(stderr, "Init the suspend signal...\n");
        if (initsuspend(SIGUSR1)) {
            perror("Failed to set up handler for SIGUSR1");
            return -1;
        }

        fprintf(stderr, "Waiting for signal...\n");
        if (simplesuspend()) {
            perror("Failed to suspend for signal\n");
            return -1;
        }

        fprintf(stderr, "Got signal...\n\n");
        if (restore()) {
            perror("Failed to restre original handler");
            return -1;
        }
    }
    return 1;
}
